#include "packet.h"

extern CSocket *csocket;
extern int sock;


extern void login_result(unsigned char result);
extern void Debug(const char *format, ...);

int count = 0;
u8 session_id[16] = {0};
u32 user_id = 0x0;
u8 security = 0x0;

char *recv_buffer = NULL;

char last_sent_trophy[16] = {0};
char last_sent_trophy_path[1024] = {0};

u8 byte0 = 0;
u8 byte1 = 0;
u8 byte2 = 0;
u8 byte3 = 0;
u8 byte4 = 0;
u8 byte5 = 0;
u8 byte6 = 0;
u8 byte7 = 0;

u32 ReadDWord(char *packet);
u16 ReadWord(char *packet);
u8 ReadByte(char *packet);
void ReadArray(char *packet, u8* out, int len);
char *sha1(const char *text);
void add_session_id(char *packet);

void process_A301(char *packet);
void process_A201(char *packet);
void process_A101(char *packet);
void build_1202( void );
void build_1203( void );
void process_6000(char *packet);
void build_6000_answer( void );

char *sha1(const char *text) {
	SHA1Context sha;
    SHA1Reset(&sha);
    SHA1Input(&sha, (const unsigned char *) text, strlen(text));
	SHA1Result(&sha);
	char lowercase[40] = "";
	int i;
	for(i = 0; i < 5 ; i++) {
		sprintf(lowercase, "%s%X", lowercase, sha.Message_Digest[i]);
		int i;
		for(i = 0; lowercase[i]; i++) lowercase[i] = tolower(lowercase[i]);
	}
	char *return_text = (char *)malloc(40);
	sprintf (return_text, "%s", lowercase);
	return return_text;
}
u8 chartohex(char ch){
	if(ch >= '0' && ch <= '9')
		return (u8)(ch - '0');

	if(ch >= 'a' && ch <= 'f')
		return (u8)(ch - 'a') + 0xA;
	
	if(ch >= 'A' && ch <= 'F')
		return (u8)(ch - 'A') + 0xA;

	return -1;
}

char hextochar(u8 ch){
	char buff[2];
	sprintf(buff, "%.1X", ch);

	return buff[0];
}

int set_crc_byte(char *packet){
	int len = strlen(packet);

	char *crc_packet = new char[len];
	memcpy(crc_packet, packet, len);

	crc_packet[10] = '0';
	crc_packet[11] = '0';

	for(int i = 0; i < len; i++)
		crc_packet[i] = toupper(crc_packet[i]);
	
	u32 crc = crc32buf(crc_packet, len);
	char crc_str[256] = {0};
	sprintf(crc_str, "%X", crc);

	char *crc_md5 = MDString(crc_str);
	
	u8 crc32 = 0;
	for(int i = 0; i < 16*2; i+=2)
		crc32 ^= ((chartohex(crc_md5[i]) << 4) + chartohex(crc_md5[i+1]) );	
	
	packet[10] = hextochar(crc32>>4);
	packet[11] = hextochar(crc32 & 0xF);

	return 1;
}

int set_security_byte(char *packet){
	u32 result = (0x3F*(~security+0x34));
	result = result ^ ( result >> 4 );
	result = result & 0xFF;
	security = result;

	packet[8] = hextochar((security>>4) & 0xF);
	packet[9] = hextochar(security & 0xF);

	return 1;
}

char* send_login(char *username, char *password){
	char path[1024] = {0};
	char packet[1024] = {0};
								
	signed char ulen = strlen(username);

	sprintf(packet, "0111%.2X000000%.2X", ulen + 16, ulen);

	for(int i = 0; i < ulen; i++){
		char buff[5];
		sprintf(buff, "%.2X", (u8)(char)(username[i]));
		strcat(packet, buff);
	}

	Debug("[LOGIN 1] %s\n", packet);
	char *sha1_pass = sha1(password);
	Debug("[SHA1] %s\n", sha1_pass);
	char *md5_pass = MDString(sha1_pass);
	strcat(packet, md5_pass);
	Debug("[MD5] %s\n", md5_pass);

	set_crc_byte(packet);	
	Debug("[LOGIN 2] %s\n", packet);
	
	sprintf(path, "/RNA/parse_packet.php?packet=%s", packet);

	csocket->remoteGET(sock, "openpstore.org", path, "PKG", NULL);	
	Debug("[GET]\n");

	char buff[1024] = {0};
	csocket->remoteRecieve(sock, buff, 1024);	

	char *buffer = csocket->clearBuffer(buff);
	Debug("[CLEAR] %s\n", buffer);
	processPacket(buffer);

	return md5_pass;
}

void processPacket(char *packet){
	count = 0;

	if(!recv_buffer)
		recv_buffer = new char[8096];
	
	u16 opcode = ReadWord(packet);
	u16 size = ReadWord(packet);

	if(((size & 0xFF00) >> 12) == 0x0 && opcode != 0x6000){

		int len = (size &  0x0FFF);
		
		int s_count = count;
		u8 decryptArray[1024] = {0};
		ReadArray(packet, decryptArray, len);
		count = s_count;
	}

	switch(opcode){
		case 0x6000:
			Debug("[OPCODE] 6000\n");
			process_6000(packet);
		break;
		case 0xA101:
			Debug("[OPCODE] A101\n");
			process_A101(packet);
		break;
		case 0xA201:
			Debug("[OPCODE] A201\n");
			process_A201(packet);
		break;
		case 0xA301:
			Debug("[OPCODE] A301\n");
			process_A301(packet);
		break;
		case 0xCCCC:
			Debug("[OPCODE] CCCC\n");
			login_result(2);
		break;
	}
}

void process_A101(char *packet){
	login_result(0);
}

void process_A301(char *packet){
	u32 num_friends = ReadDWord(packet);
		Debug("NUM: %X\n", num_friends);
	for(u32 i = 0; i < num_friends; i++){
		Debug("ID: %X\n", ReadDWord(packet));
		u8 nick_len = ReadByte(packet); 
		char *nick = new char[nick_len];
		
		for(u8 n = 0; n < nick_len; n++)
			nick[n] = (char)ReadByte(packet);
		nick[nick_len] = '\0';

		Debug("NICK: %s\n", nick);
	}
}

void process_A201(char *packet){
	u8 flag = ReadByte(packet);

	if(flag == 0x0)
		build_1202();
	else
		build_1203();
}

void build_1202(){
	char packet[1024] = {0};
	char packet_raw[1024] = {0};
	sprintf(packet, "021216B00000");
	add_session_id(packet);

	char tropusr_path[1024] = {0};
	sprintf(tropusr_path, "%s\\TROPUSR.DAT", last_sent_trophy_path);

	tropusr_ret *ret = parse_tropusr(tropusr_path);
	
	strcat(packet_raw, last_sent_trophy);
	packet_raw[16] = ret->max_trophies;	

	int size = 17;

	while(ret->trophies_list){
		packet_raw[size] = ret->trophies_list->trophie->trophie_id;
		
		packet_raw[size+1] = ret->trophies_list->trophie->unlocked;
		
		packet_raw[size+2] = ret->trophies_list->trophie->platinum_unlocked;

		ret->trophies_list = ret->trophies_list->next;

		size += 3;
	}

	for(int i = 0; i < size; i++){
		char buff[4] = {0};
		sprintf(buff, "%.2X", (u8)(packet_raw[i]));
		strcat(packet, buff);
	}

	set_security_byte(packet);
	set_crc_byte(packet);

	char path[1024] = {0};
	sprintf(path, "/RNA/parse_packet.php?packet=%s", packet);
	csocket->remoteGET(sock, "openpstore.org", path, "PKG", NULL);

	Debug("[C->S] %s\n", packet);
	
	csocket->remoteRecieve(sock, recv_buffer, 8096);
	

	//LET'S SEND TROPCONF! =)
	char tropconf_path[1024] = {0};
	sprintf(tropconf_path, "%s\\TROPCONF.SFM", last_sent_trophy_path);

	FILE *fp = fopen(tropconf_path, "rb");
	fseek(fp, 0, SEEK_END);
	int fsize = ftell(fp);
	rewind(fp);

	char *buff = (char*)malloc(fsize + 5 - 0x1A1);
	memset(buff, 0, fsize+5 - 0x1A1);
	strcpy(buff, "info=");

	fseek(fp, 0x1A1, SEEK_CUR);
	fread((buff+5), 1, fsize - 0x1A1, fp);
	
	char packet2[1024] = {0};
	strcpy(packet2, "041200000000");
	add_session_id(packet2);
	set_security_byte(packet2);
	set_crc_byte(packet2);

	sprintf(path, "/RNA/parse_packet.php?len=%d&packet=%s", fsize - 0x1A1, packet2);
	csocket->remotePOST(sock, "openpstore.org", path, buff, "PKG", NULL);
	
	csocket->remoteRecieve(sock, recv_buffer, 8096);

	free(buff);
}
void build_1203(){
	char packet[1024] = {0};
	char packet_raw[1024] = {0};
	sprintf(packet, "031216B00000");
	add_session_id(packet);

	char tropusr_path[1024] = {0};
	sprintf(tropusr_path, "%s\\TROPUSR.DAT", last_sent_trophy_path);

	tropusr_ret *ret = parse_tropusr(tropusr_path);
	
	strcat(packet_raw, last_sent_trophy);
	packet_raw[16] = ret->user_trophies;	

	if(ret->user_trophies == 0)
		return;

	int size = 17;	
	while(ret->trophies_list){
		if(ret->trophies_list->trophie->unlocked == 0x1){
			packet_raw[size] = ret->trophies_list->trophie->trophie_id;
		
			packet_raw[size+1] = ret->trophies_list->trophie->unlocked;
		
			packet_raw[size+2] = ret->trophies_list->trophie->platinum_unlocked;
					
			size += 3;
		}
		ret->trophies_list = ret->trophies_list->next;
	}
	
	for(int i = 0; i < size; i++){
		char buff[4] = {0};
		sprintf(buff, "%.2X", (u8)(packet_raw[i]));
		strcat(packet, buff);
	}

	set_security_byte(packet);
	set_crc_byte(packet);

	char path[1024] = {0};
	sprintf(path, "/RNA/parse_packet.php?packet=%s", packet);
	csocket->remoteGET(sock, "openpstore.org", path, "PKG", NULL);

	Debug("[C->S] %s\n", packet);
	
	csocket->remoteRecieve(sock, recv_buffer, 8096);
}

void process_6000(char *packet){
	u8 flag = ReadByte(packet);

	switch(flag){
		case 0x50:{
			Debug("[FLAG] 50\n");
			security = ReadByte(packet);
			u8 result = ReadByte(packet); //0x5 -> Login succes

			if(result != 0x5)
				login_result(0);

			ReadByte(packet); //0x1 -> User permisses
			user_id = ReadDWord(packet); //save uid
			ReadArray(packet, session_id, 16); //save sid

			login_result(1);
		}break;
	}
}

void add_session_id(char *packet){
	for(int i = 0; i < 16; i++){
		char buff[3];
		sprintf(buff, "%.2X", session_id[i]);
		strcat(packet, buff);
	}
}

void listFriends(){
	Debug("\nRequesting Friends List\n");
	
	char packet[1024] = "011300000000";
	add_session_id(packet);
	set_security_byte(packet);
	set_crc_byte(packet);

	char path[1024] = {0};
	sprintf(path, "/RNA/parse_packet.php?packet=%s", packet);
	csocket->remoteGET(sock, "openpstore.org", path, "PKG", NULL);
	
	Debug("[C->S] %s\n", packet);

	csocket->remoteRecieve(sock, recv_buffer, 8096);
	bool end = false;
	bool old = false;
	while(!end){
		if(*recv_buffer == '\n'){
			if(!old)
				old = true;
			else
				end = true;
		}else if(*recv_buffer == '\r'){}
		else
			old = false;	
		
		recv_buffer++;
	}
	
	Debug("[S->C] %s\n", recv_buffer);
	processPacket(recv_buffer);
}

void addFriend(){
	char packet[1024] = {0};

	strcpy(packet, "021300000000");
	add_session_id(packet);
	set_security_byte(packet);

	char buff[100];
	sprintf(buff, "04%.2X%.2X%.2X%.2X", 't', 'e', 's', 't');
	strcat(packet, buff);

	set_crc_byte(packet);

	char path[2048] = {0};
	sprintf(path, "/RNA/parse_packet.php?packet=%s", packet);
	csocket->remoteGET(sock, "openpstore.org", path, "PKG", NULL);
	csocket->remoteRecieve(sock, recv_buffer, 8096);	
	bool end = false;
	bool old = false;
	while(!end){
		if(*recv_buffer == '\n'){
			if(!old)
				old = true;
			else
				end = true;
		}else if(*recv_buffer == '\r'){}
		else
			old = false;	
		
		recv_buffer++;
	}

	Debug("[S->C] %s\n", recv_buffer);
}

void syncTrophies(){
	Debug("Syncing Trophies\n");
	char packet[1024] = {0};
								//5 = ulen
	sprintf(packet, "011216000000");
	add_session_id(packet);

	DIR *dir, *sub_dir1;
    struct dirent *ent, *ent1;

    /* open directory stream */
	dir = opendir ("I:\\home");
	if (dir != NULL) {
		while ((ent = readdir (dir)) != NULL) {
			if(strcmp(ent->d_name, ".") == 0)
				continue;
			else if(strcmp(ent->d_name, "..") == 0)
				continue;

			char path[1024] = {0};
			sprintf(path, "I:\\home\\%s\\trophy", ent->d_name);

			sub_dir1 = opendir (path);
			if (sub_dir1 != NULL) {
				while ((ent1 = readdir (sub_dir1)) != NULL) {
					if(strcmp(ent1->d_name, ".") == 0)
						continue;
					else if(strcmp(ent1->d_name, "..") == 0)
						continue;
					else if(strcmp(ent1->d_name, "_TROPSYS_") == 0)
						continue;

					sprintf(last_sent_trophy_path, "%s\\%s", path, ent1->d_name);
					strcpy(last_sent_trophy, ent1->d_name);

					char send_packet[1024] = {0};
					strcpy(send_packet, packet);
					
					for(unsigned int i = 0; i< 16; i++){
						char buff[3] = {0};
						if(i < strlen(ent1->d_name))
							sprintf(buff, "%.2X", (u8)(ent1->d_name[i]));
						else
							strcpy(buff, "00");
						strcat(send_packet, buff);
					}
					
					set_security_byte(send_packet);
					set_crc_byte(send_packet);

					char server_path[1024] = {0};
					sprintf(server_path, "/RNA/parse_packet.php?packet=%s", send_packet);
					csocket->remoteGET(sock, "openpstore.org", server_path, "PKG", NULL);

					Debug("[C->S] %s\n", send_packet);

					csocket->remoteRecieve(sock, recv_buffer, 8096);
					bool end = false;
					bool old = false;
					while(!end){
						if(*recv_buffer == '\n'){
							if(!old)
								old = true;
							else
								end = true;
						}else if(*recv_buffer == '\r'){}
						else
							old = false;	
		
						recv_buffer++;
					}
	
					Debug("[S->C] %s\n", recv_buffer);
					processPacket(recv_buffer);
				}
			}
			
			closedir (sub_dir1);
		}
      
		closedir (dir);
	}
}

u8 ReadByte(char *packet){
	u8 ret = (chartohex(packet[count]) << 4) + chartohex(packet[count + 1]);

	count += 1*2;
	return ret;
}

u16 ReadWord(char *packet){
	u16 ret = ((chartohex(packet[count]) << 4) + chartohex(packet[count + 1])) + (((chartohex(packet[count + 2]) << 4) + chartohex(packet[count + 3])) << 8);

	count += 2*2;
	return ret;
}

u32 ReadDWord(char *packet){
	u32 ret = ((chartohex(packet[count]) << 4) + chartohex(packet[count + 1])) + (((chartohex(packet[count + 2]) << 4) + chartohex(packet[count + 3])) << 8) + (((chartohex(packet[count + 4]) << 4) + chartohex(packet[count + 5])) << 16) + (((chartohex(packet[count + 6]) << 4) + chartohex(packet[count + 7])) << 24);

	count += 4*2;
	return ret;
}

void ReadArray(char *packet, u8* out, int len){
	for(int i = 0; i < len; i++)
		out[i] = ReadByte(packet);
}